#include <proc.h>
#include <elf.h>
#include <fs.h>
#ifdef __LP64__
# define Elf_Ehdr Elf64_Ehdr
# define Elf_Phdr Elf64_Phdr
#else
# define Elf_Ehdr Elf32_Ehdr
# define Elf_Phdr Elf32_Phdr
#endif
// extern size_t ramdisk_read(void *buf, size_t offset, size_t len);
// extern size_t ramdisk_write(const void *buf, size_t offset, size_t len);
extern int fs_open(const char *pathname, int flags, int mode);
extern size_t fs_read(int fd, void *buf, size_t len);
extern int fs_close(int fd);
extern size_t fs_lseek(int fd, size_t offset, int whence);

static uintptr_t loader(PCB *pcb, const char *filename) {
  // TODO();
  int fd = fs_open(filename, 0, 0);
  Elf_Ehdr elf_header;
  size_t len;
  // len = ramdisk_read(&elf_header, 0, sizeof(Elf_Ehdr));
  
  fs_lseek(fd, 0, SEEK_SET);
  len = fs_read(fd, &elf_header, sizeof(Elf_Ehdr));
  if(len != sizeof(Elf_Ehdr)){
    assert(0);
  }
  assert(memcmp(elf_header.e_ident, ELFMAG, SELFMAG) == 0);
  Elf_Phdr segments[elf_header.e_phnum];
  // read all segment
  // if(ramdisk_read(segments, elf_header.e_phoff, sizeof(segments)) != sizeof(segments)){
  //   assert(0);
  // }
  fs_lseek(fd, elf_header.e_phoff, SEEK_SET);
  if(fs_read(fd, segments, sizeof(segments)) != sizeof(segments)){
    assert(0);
  }
  
  for(int i = 0; i < elf_header.e_phnum; i++){
    if(segments[i].p_type == PT_LOAD){
      // printf("The segement address is %x, the filesize is %x\n",segments[i].p_vaddr, segments[i].p_filesz);
      // ramdisk_read((void*)segments[i].p_vaddr, segments[i].p_offset, segments[i].p_filesz);
      fs_lseek(fd, segments[i].p_offset, SEEK_SET);
      fs_read(fd, (void *)segments[i].p_vaddr, segments[i].p_filesz);
      memset((void*)(segments[i].p_vaddr+segments[i].p_filesz), 0,segments[i].p_memsz-segments[i].p_filesz);
    }
  }
  fs_close(fd);
  return elf_header.e_entry;
}

void naive_uload(PCB *pcb, const char *filename) {
  uintptr_t entry = loader(pcb, filename);
  Log("Jump to entry = %p", entry);
  ((void(*)())entry) ();
}

